package com.chen.function_demo.chapter4;

import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * @author 陈哲<p>
 * ================================<p>
 * Date: 2025/5/12<p>
 * Time: 13:55<p>
 * ================================
 */
public class Section1 {
    /*
        函数式编程的实际应用：
        1、数据统计分析（如Stream API）
        2、异步处理（如CompletableFuture）
        3、框架设计
        4、并行计算
        5、UI事件
        关注的重点是前三项
        此处就重点延伸一个CompletableFuture
     */
    public static void main(String[] args) throws IOException {
        //System.out.println(Thread.currentThread().getName() + "进入main方法，模拟执行任务");
        // 同步方法
        //test();
        // 异步方法
        //new Thread(Section1::test).start(); // 使用lambda表达式（Runnable函数，无参无返回值），调用test方法
        //System.out.println(Thread.currentThread().getName() + "模拟任务时，我还想同时做其他事情");

        // 在实际开发中，不推荐自己创建线程，而是使用线程池
        // 原因：1、资源消耗大：创建线程和销毁需要耗费系统资源，如创建线程需要耗费CPU资源，创建线程需要耗费内存资源
        //      2、如果任务量较大，new Thread() 可能会导致 无限创建线程，耗尽系统资源，引发 OutOfMemoryError
        //      3、手动创建的线程难以统一管理，无法有效监控、调度或关闭。
        //      。。。。。。
        // 创建线程池：1、通过Executors的静态方法创建一些固定的线程池
        //           2、通过ThreadPoolExecutor创建线程池
        //  Executors.newFixedThreadPool(10);  // 创建一个固定大小的线程池
        //  Executors.newCachedThreadPool();  // 创建一个可缓存的线程池
        //  Executors.newScheduledThreadPool(10);  等等
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,  // 核心线程数
                20,  // 最大线程数
                60,  // 线程存活时间
                TimeUnit.SECONDS,  // 线程存活时间单位
                new ArrayBlockingQueue<>(100),  // 阻塞队列
                new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略
        );
        //threadPoolExecutor.submit(Section1::test);
        // 在test方法中，模拟运算的结果由自己消费，所以不需要返回值。现在可能处理结果的情况有多种，需要将结果返回，由调用方处理消费
        // 做法1: 在外层处理结果 本质还是Runnable接口 无参无返回值
        /*threadPoolExecutor.submit(
                () -> {
                    int i = test2();
                    System.out.println("消费异步结果" + i);
                }
        );*/
        // 做法2: 将处理逻辑封装成函数对象，传入返回结果的方法里，即实现了应对多种处理逻辑，也不需要每次在获取结果后再进行逻辑处理
        // 本质还是使用Runnable接口 无参无返回值
        // threadPoolExecutor.submit(() -> test3(i -> System.out.println("消费异步结果" + i)));
        // 方法2就带了一个问题，就是此为函数套函数，函数复杂的情况下，代码可读性很差
        // 由此引申出CompletableFuture
        // 执行异步任务
        //CompletableFuture.runAsync(); // 任务不需要返回结果
        //CompletableFuture.supplyAsync(); // 任务需要返回结果
        // CompletableFuture 默认使用的内部线程是守护线程


        // 主线程阻塞，等待子线程执行完成,因为 CompletableFuture默认使用的是守护线程，所有需要主线程不结束
        /*
            CompletableFuture处理异步任务结果
            thenApply(Function function) 转化结果
            thenApplyAsync() 异步转化结果
            thenAccept(Consumer consumer) 消费结果
            thenAcceptAsync() 异步消费结果
         */
        CompletableFuture.supplyAsync(Section1::test2)
                .thenAccept(i -> System.out.println("消费异步结果" + i)); //用then xxx来消费结果
        System.in.read();

    }

    public static void test() {
        System.out.println(Thread.currentThread().getName() + "进入test方法，线程开始休眠");
        try {
            Thread.sleep(1000);
            int c = 1 + 1; // 模拟计算
            System.out.println(Thread.currentThread().getName() + "test方法中，计算结果为：" + c); // 模拟消费运算结果
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "test方法结束，线程休眠结束");
    }

    public static int test2() {
        System.out.println(Thread.currentThread().getName() + "进入test方法，线程开始休眠");
        int c = 0;
        try {
            Thread.sleep(1000);
            c = 1 + 1; // 模拟计算
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "test方法结束，线程休眠结束");
        return c;
    }

    /**
     * 将处理逻辑封装成函数对象，传入返回结果的方法里
     * 1、函数对象需要入参，但是处理完成后就结束了，所以不需要返回值，即函数时个Consumer函数
     */
    public static void test3(Consumer<Integer> consumer) {
        System.out.println(Thread.currentThread().getName() + "进入test方法，线程开始休眠");
        int c;
        try {
            Thread.sleep(1000);
            c = 1 + 1; // 模拟计算
            consumer.accept(c);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "test方法结束，线程休眠结束");
    }
}
